package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.common.constants.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.ArticleSearchService;
import com.heima.utils.thread.AppUserThreadLocalUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ArticleSearchServiceImpl2 {
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ApUserSearchService apUserSearchService;

    /**
     * app端文章关键字查询
     *
     * @param dto
     * @return
     */
    public ResponseResult search(UserSearchDto dto) throws IOException {
        //1.参数校验
        if (dto == null || StringUtils.isEmpty(dto.getSearchWords())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.es查询
        //2.1 准备searchRequest
        SearchRequest searchRequest = new SearchRequest(ArticleConstants.ES_INDEX);
        //2.2 准备boolean查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //2.3 利用布尔查询进行关键字搜索  must参与算分
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(dto.getSearchWords(), "title", "content"));
        //2.4 (发布时间)范围查询 过滤 不参与算分
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        //2.5 布尔查询封装到indexRequest中
        searchRequest.source().query(boolQueryBuilder);
        //2.6 分页参数
        searchRequest.source().from(0);
        searchRequest.source().size(dto.getPageSize());
        //2.7 设置排序字段
        searchRequest.source().sort("publishTime", SortOrder.DESC);
        //2.8 高亮查询设置
        //2.8.1 创建高亮设置对象
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //2.8.2 高亮字段 标题
        highlightBuilder.field("title");
        //2.8.3 高亮标签 和前端约定好的
        highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        //2.8.4 高亮查询封装到searchRequest
        searchRequest.source().highlighter(highlightBuilder);
        //3 发送请求得到返回结果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //4 解析结果 (一层一层读取)
        SearchHit[] hits = searchResponse.getHits().getHits();
        //4.1 准备一个集合封装结果数据
        List<Map> list = new ArrayList<>();
        //4.2 遍历解析结果
        for (SearchHit hit : hits) {
            //4.3 转成json对象
            String json = hit.getSourceAsString();
            //4.4 解析成map集合 方便操作
            Map map = JSON.parseObject(json, Map.class);
            //4.5 高亮字段的处理
            if (hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0) {
                //4.6 得到高亮字段
                Text[] titles = hit.getHighlightFields().get("title").getFragments();
                //4.7 转为字符串
                String title = StringUtils.join(titles);// 只有一个  不需要,
                //4.8 高亮字段替换原来字段
                map.put("h_title",title); // 这也是和前端约定好的
            }else {
                //4.9 没有高亮字段
                map.put("h_title",map.get("title"));
            }

            //4.10 每次遍历结束 将数据添加到集合封装
            list.add(map);
        }
        //5 异步调用,保存搜索结果
        //5.1 先得到登录用户id
        Integer userId = AppUserThreadLocalUnit.get();
        //5.2 判断是否得登录了 并且是第一次
        if (userId != null && dto.getFromIndex() == 0) {
            apUserSearchService.insert(dto.getSearchWords(),userId);
        }
        //6 返回结果
        return ResponseResult.okResult(list);
    }
}
